package javaLabs.lab1.ListQueue;


import java.io.*;
import java.util.StringTokenizer;
import java.util.NoSuchElementException;

/**
 *  Class which gets queue's operation from file, executes them and writes result to another file
 *  @author Mixser
 */
public class QueueScenario {

    private BufferedReader input = null;
    private BufferedWriter output = null;
    private StringTokenizer tokenizer = null;

    private ListQueue queue = null;

    /**
     * Class constructor which initialize data
     * @param inputPath filePath for input file
     * @param outputPath filePath for output file
     * @throws QueueScenarioFileOpeningFailedException, QueueScenarioFileFailedException if something wrong with IO operations
     */
    public QueueScenario(String inputPath, String outputPath) throws IOException {
        try {
            input = new BufferedReader(new FileReader(inputPath));
            output = new BufferedWriter(new FileWriter(outputPath));
            queue = new ListQueue();
        } catch (IOException e) {
            clearAll(); // делаем чистку того, что можно и нужно почистить
            throw new QueueScenarioFileOpeningFailedException("Failed to open file!", e); // передаем исключение дальше, чтобы Main обработал его
        }
    }

    /**
     * clear input file
     * @throws IOException if something wrong with IO operations
     */
    private void closeInput() throws IOException {
        if (input != null) {
            input.close();
            input = null;
        }
    }

    /**
     * clear output file
     * @throws IOException if something wrong with IO operations
     */
    private void closeOutput() throws IOException {
        if (output != null) {
            output.close();
            output = null;
        }
    }

    /**
     * clear queue
     */
    private void clearQueue() {
        queue = null;
    }

    /**
     * clear io's buffers and queue
     * @throws QueueScenarioFileFailedException if something wrong with IO operations
     */
    private void clearAll() throws QueueScenarioFileFailedException {
        clearQueue();

        QueueScenarioInputCloseFailedException inputException = null;
        QueueScenarioOutputCloseFailedException outputException = null;

        try {
            closeInput();
        } catch (IOException e) {
            inputException = new QueueScenarioInputCloseFailedException("Failed to close input file!", e);
        }

        try {
            closeOutput();
        } catch (IOException e) {
            outputException = new QueueScenarioOutputCloseFailedException("Failed to close output file!", e);
        }

        if (inputException != null || outputException != null) {
            throw  new QueueScenarioFileFailedException(inputException, outputException);
        }
    }

    /**
     * Execute commands and write results of executing.
     * After all - clear buffers and queue.
     * @throws IOException if something wrong with IO operations
     */
    public void startPlayScenario() throws IOException {
        try {
            String currentLine = "";
            Integer element = null;

            while ((currentLine = input.readLine()) != null) {
                tokenizer = new StringTokenizer(currentLine);
                boolean needNextLine = false;
                switch (tokenizer.nextToken()) {
                    case "size":
                        output.write(Integer.toString(queue.size()));
                        needNextLine = true;
                        break;
                    case "isEmpty":
                        output.write(Boolean.toString(queue.isEmpty()));
                        needNextLine = true;
                        break;
                    case "clear":
                        queue.clear();
                        break;
                    case "toString":
                        output.write(queue.toString());
                        needNextLine = true;
                        break;
                    case "offer":
                        if (!tokenizer.hasMoreTokens()) {
                            throw new IncorrectSyntaxException("Empty string!");
                        }
                        element = Integer.decode(tokenizer.nextToken());
                        queue.offer(element);
                        break;
                    case "add":
                        if (!tokenizer.hasMoreTokens()) {
                            throw new IncorrectSyntaxException("Empty string!");
                        }
                        element = Integer.decode(tokenizer.nextToken());
                        queue.add(element);
                        break;
                    case "peek":
                        element = queue.peek();
                        output.write(element != null ? Integer.toString(element) : "null");
                        needNextLine = true;
                        break;
                    case "element":
                        element = queue.element();
                        output.write(Integer.toString(element)); // element при size == 0 кидает NoSuchElementException
                        needNextLine = true;
                        break;
                    case "poll":
                        element = queue.poll();
                        output.write(element != null ? Integer.toString(queue.peek()) : "null");
                        needNextLine = true;
                        break;
                    case "remove":
                        element = queue.remove();
                        output.write(Integer.toString(element)); // remove при size == 0 кидает NoSuchElementException
                        needNextLine = true;
                        break;
                }
                if (needNextLine) {
                    output.newLine();
                }
            }
        }
        catch (NumberFormatException e) {
            writeErrorInFile("Incorrect value in second parameter: must be integer! " + e.getMessage());
        }
        catch (IncorrectSyntaxException e) {
            writeErrorInFile("Incorrect value in second parameter: " + e.getMessage());
        }
        catch (NoSuchElementException e) {
            writeErrorInFile(e.getMessage());
        }
        finally {
            tokenizer = null;
            clearAll(); // больше нам ничего не нужно, можно чистить файлы
        }
    }

    /**
     * Write error in file
     * @param error string message of error
     * @throws QueueScenarioFileWritingFailedException if something wrong with IO operations
     */
    public void writeErrorInFile(String error) throws QueueScenarioFileWritingFailedException {
        try {
            output.write(error);
            output.newLine();
        } catch (IOException e) {
            throw new QueueScenarioFileWritingFailedException("Failed to write data to output file!", e);
        }
    }
}
